package com.neusoft.single;

import com.neusoft.entity.Hero;

import java.util.Objects;
import java.util.Stack;

/**
 * @author lichunlong lcl253137@alibaba-inc.com
 * @desc 单向链表实现类: 管理水浒英雄
 * @date 2024-04-15
 */
public class SingleLinkedList {

    /**
     * 先初始化一个头节点, 头节点不动, 不存放具体的数据
     */
    private Node<Hero> headNode = new Node<>();

    /**
     * 添加节点到单向链表 (不考虑编号顺序): 找到当前链表的最后节点, 将最后这个节点的next指向新的节点
     *
     * @param node 新增节点
     */
    public void add(Node<Hero> node) {
        // 因为head节点不能动，因此需要一个tempNode辅助节点
        Node<Hero> tempNode = headNode;

        while (tempNode.next != null) {
            // 如果没有找到最后, 将将temp后移
            tempNode = tempNode.next;
        }

        // 当退出while循环时，temp就指向了链表的最后; 将最后这个节点的next指向新的节点
        tempNode.next = node;
    }

    /**
     * 顺序添加: 在添加英雄时，根据排名将英雄插入到指定位置
     *
     * @param node 新增节点
     */
    public void orderAdd(Node<Hero> node) {
        // 因为头节点不能动，仍需要通过一个辅助变量帮助找到添加的位置
        Node<Hero> tempNode = headNode;
        // 判断添加的编号是否已存在
        boolean existsFlag = false;
        while (Objects.nonNull(tempNode.next)) {
            if (tempNode.next.getItem().getNo() > node.getItem().getNo()) {
                // 位置找到，就在temp的后面插入
                break;
            } else if (tempNode.next.getItem().getNo() == node.getItem().getNo()) {
                // 说明希望添加的heroNode的编号已然存在
                existsFlag = true;
                break;
            }

            // 后移，遍历当前链表
            tempNode = tempNode.next;
        }

        // 不能添加则说明编号存在
        if (existsFlag) {
            System.out.printf("准备插入的英雄的编号%d已经存在了, 不能加入\n", node.getItem().getNo());
            return;
        }

        // 插入到链表中, temp的后面
        node.next = tempNode.next;
        tempNode.next = node;
    }

    /**
     * 更新节点: 根据Hero#no编号来修改，即Hero#no编号作为唯一主键
     *
     * @param heroNode 水浒英雄节点
     */
    public void update(Node<Hero> heroNode) {
        // 判断链表是否为空
        if (Objects.isNull(headNode.next)) {
            System.out.println("链表为空, 无法更新");
            return;
        }

        // 因为headNode头节点不能动，因此需要一个tempNode辅助节点
        Node<Hero> tempNode = headNode.next;
        boolean updateFlag = false;
        while (Objects.nonNull(tempNode)) {
            if (tempNode.getItem().getNo() == heroNode.getItem().getNo()) {
                // 根据Hero#no编号已经找到对应节点
                tempNode.setItem(heroNode.getItem());
                updateFlag = true;
                break;
            }
            // 后移tempNode
            tempNode = tempNode.next;
        }

        System.out.println(updateFlag ? "更新成功" : "未成功找到数据, 无法更新");
    }

    /**
     * 删除节点
     *
     * @param heroNodeNo 水浒英雄no编号
     */
    public void delete(Integer heroNodeNo) {
        // 判断链表是否为空
        if (Objects.isNull(headNode.next)) {
            System.out.println("链表为空, 无法删除");
            return;
        }

        // 因为headNode头节点不能动，因此需要一个tempNode辅助节点
        Node<Hero> tempNode = headNode;
        boolean deleteFlag = false;
        while (Objects.nonNull(tempNode.next)) {
            // 根据当前节点的下一个节点已经找到对应Hero#no编号
            if (tempNode.next.getItem().getNo() == heroNodeNo) {
                tempNode.next = tempNode.next.next;

                deleteFlag = true;
                break;
            }
            // 后移tempNode
            tempNode = tempNode.next;
        }

        System.out.println(deleteFlag ? "删除成功" : "未成功找到数据, 无法删除");
    }

    /**
     * 翻转单链表
     */
    public void reverse() {
        // 如果当前链表为空，或者只有一个节点，无需反转，直接返回
        if (Objects.isNull(headNode.next) || Objects.isNull(headNode.next.next)) {
            System.out.println("如果当前链表为空，或者只有一个节点，无需反转，直接返回");
            return;
        }

        Node<Hero> reverseHeadNode = new Node<>();
        Node<Hero> currentNode = headNode.next;
        Node<Hero> nextNode;
        while (Objects.nonNull(currentNode)) {
            // 先暂时保存当前节点的下一个节点
            nextNode = currentNode.next;
            // 将当前节点的下一个节点指向新链表的最前端
            currentNode.next = reverseHeadNode.next;
            reverseHeadNode.next = currentNode;

            // 原单链表往后移动
            currentNode = nextNode;
        }

        // 将head.next指向reverseHead.next,实现单链表的反转
        headNode.next = reverseHeadNode.next;
    }

    /**
     * 倒序打印单链表: 利用stack栈实现: 现金后出
     */
    public void reversePrint() {
        if (Objects.isNull(headNode.next)) {
            System.out.println("如果当前链表为空，无需打印，直接返回");
            return;
        }
        Stack<Node<Hero>> stack = new Stack<>();
        Node<Hero> currentNode = headNode.next;
        while (Objects.nonNull(currentNode)) {
            stack.push(currentNode);
            // 当前节点后移
            currentNode = currentNode.next;
        }

        while (!stack.empty()) {
            System.out.println(stack.pop());
        }
    }

    /**
     * 显示链表中的数据
     */
    public void list() {
        // 判断链表是否为空
        if (Objects.isNull(headNode.next)) {
            System.out.println("链表为空");
            return;
        }
        // 因为headNode头节点不能动，因此需要一个tempNode辅助节点
        Node<Hero> tempNode = headNode.next;
        while (Objects.nonNull(tempNode)) {
            // 输出节点的信息
            System.out.println(tempNode);
            // 后移tempNode
            tempNode = tempNode.next;
        }
    }

    /**
     * 获取链表中的节点个数(不包含头节点)
     *
     * @return 节点个数
     */
    public Integer length() {
        Integer length = 0;
        // 因为headNode头节点不能动，因此需要一个tempNode辅助节点
        Node<Hero> tempNode = headNode;

        while (Objects.nonNull(tempNode.getNext())) {
            length++;
            tempNode = tempNode.getNext();
        }

        return length;
    }

    /**
     * 根据倒数索引获取链表中的节点
     * 注意：倒数索引从1开始，例如，倒数索引为1表示获取链表的最后一个节点
     * 如果链表中的节点个数小于指定的倒数索引，则返回null
     *
     * @param countDownIndex 指定的倒数索引
     * @return 返回在链表中对应倒数索引的节点，如果不存在则返回null
     */
    public Node<Hero> getCountDownNode(Integer countDownIndex) {
        if (this.length() <= 0 || countDownIndex <= 0 || this.length() < countDownIndex) {
            System.out.println("链表为空，或输入索引数字非法，或输入索引数字超出范围，无法查询");
            return null;
        }

        int index = this.length() - countDownIndex + 1;
        // 因为headNode头节点不能动，因此需要一个tempNode辅助节点
        Node<Hero> tempNode = headNode;

        int tempIndex = 0;
        while (tempIndex < index) {
            tempIndex++;
            tempNode = tempNode.getNext();
        }

        return tempNode;
    }
}
